Skip to content

InstCombine: fold(select C, (X | A), X) | B into X | select C, (A | B), B. (#154246) #154267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

badwriter123
Copy link

@badwriter123 badwriter123 commented Aug 19, 2025

Fixes #154246
Hello,

I noticed that the InstCombine was missing a simple factoring opportunity when a select feeds a bitwise-or.
For Example in patterns like :

  1. (select C, (X | A), X) | B
  2. (select C, X, (X | A)) | B
    we can pull the X out and rewrite the canonical and optimized form as follows :
  3. X | select C, (A | B), B
  4. X | select C, B, (A | B)

I added a fold in visitOr that recognizes those shapes (including the commuted outer-or) and performs the required rewrite.
and also I placed it before the generic "push constant into select arms" fold so we dont lose the structure we need to factor the X cleanly.

I have also added a test at llvm/test/Transforms/InstCombine/or-select-factor.ll to cover this transformation.

No other files or changes were made.

@badwriter123 badwriter123 requested a review from nikic as a code owner August 19, 2025 04:41
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Aug 19, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 19, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Rohan A M (badwriter123)

Changes

#154246
Hello,

I noticed that the InstCombine was missing a simple factoring opportunity when a select feeds a bitwise-or.
For Example in patterns like :

  1. (select C, (X | A), X) | B
  2. (select C, X, (X | A)) | B
    we can pull the X out and rewrite the canonical and optimized form as follows :
  3. X | select C, (A | B), B
  4. X | select C, B, (A | B)

I added a fold in visitOr that recognizes those shapes (including the commuted outer-or) and performs the required rewrite.
and also I placed it before the generic "push constant into select arms" fold so we dont lose the structure we need to factor the X cleanly.

I have also added a test at llvm/test/Transforms/InstCombine/or-select-factor.ll to cover this transformation.

No other files or changes were made.


Full diff: https://github.com/llvm/llvm-project/pull/154267.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+70)
  • (added) llvm/test/Transforms/InstCombine/or-select-factor.ll (+33)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 6e46898634070..ac78b0276d594 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2695,6 +2695,40 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
   if (Instruction *FoldedLogic = foldBinOpIntoSelectOrPhi(I))
     return FoldedLogic;
 
+  // Factor a common operand across select feeding an 'or':
+  // (select Cond, (X | A), X) | B  ->  X | select Cond, (A | B), B
+  // (select Cond, X, (X | A)) | B  ->  X | select Cond, B, (A | B)
+  // and commuted variants swapping the operands of the outer 'or'.
+  auto TryFactorSelectOr = [&](Value *SelOp, Value *OtherOp) -> Instruction * {
+    auto *SI = dyn_cast<SelectInst>(SelOp);
+    if (!SI)
+      return nullptr;
+
+    Value *Cond = SI->getCondition();
+    Value *T = SI->getTrueValue();
+    Value *F = SI->getFalseValue();
+    Value *X, *A;
+
+    // Match: select Cond, (X|A), X
+    if (match(T, m_c_Or(m_Value(X), m_Value(A))) && F == X) {
+      Value *AorB = Builder.CreateOr(A, OtherOp);
+      Value *InnerSel = Builder.CreateSelect(Cond, AorB, OtherOp);
+      return BinaryOperator::CreateOr(X, InnerSel);
+    }
+    // Match: select Cond, X, (X|A)
+    if (match(F, m_c_Or(m_Value(X), m_Value(A))) && T == X) {
+      Value *AorB = Builder.CreateOr(A, OtherOp);
+      Value *InnerSel = Builder.CreateSelect(Cond, OtherOp, AorB);
+      return BinaryOperator::CreateOr(X, InnerSel);
+    }
+    return nullptr;
+  };
+
+  if (Instruction *R = TryFactorSelectOr(Op0, Op1))
+    return R;
+  if (Instruction *R = TryFactorSelectOr(Op1, Op0))
+    return R;
+
   if (Instruction *DeMorgan = matchDeMorgansLaws(I, *this))
     return DeMorgan;
 
@@ -4022,6 +4056,42 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
     }
   }
 
+  // Factor a common operand across select feeding an 'or' before pushing the
+  // constant into the select arms:
+  // (select Cond, (X | A), X) | B  ->  X | select Cond, (A | B), B
+  // (select Cond, X, (X | A)) | B  ->  X | select Cond, B, (A | B)
+  // and commuted variants swapping the operands of the outer 'or'.
+  auto TryFactorSelectOrEarly = [&](Value *SelOp,
+                                    Value *OtherOp) -> Instruction * {
+    auto *SI = dyn_cast<SelectInst>(SelOp);
+    if (!SI)
+      return nullptr;
+
+    Value *Cond = SI->getCondition();
+    Value *T = SI->getTrueValue();
+    Value *F = SI->getFalseValue();
+    Value *X, *A;
+
+    // Match: select Cond, (X|A), X
+    if (match(T, m_c_Or(m_Value(X), m_Value(A))) && F == X) {
+      Value *AorB = Builder.CreateOr(A, OtherOp);
+      Value *InnerSel = Builder.CreateSelect(Cond, AorB, OtherOp);
+      return BinaryOperator::CreateOr(X, InnerSel);
+    }
+    // Match: select Cond, X, (X|A)
+    if (match(F, m_c_Or(m_Value(X), m_Value(A))) && T == X) {
+      Value *AorB = Builder.CreateOr(A, OtherOp);
+      Value *InnerSel = Builder.CreateSelect(Cond, OtherOp, AorB);
+      return BinaryOperator::CreateOr(X, InnerSel);
+    }
+    return nullptr;
+  };
+
+  if (Instruction *R = TryFactorSelectOrEarly(Op0, Op1))
+    return R;
+  if (Instruction *R = TryFactorSelectOrEarly(Op1, Op0))
+    return R;
+
   if (Instruction *FoldedLogic = foldBinOpIntoSelectOrPhi(I))
     return FoldedLogic;
 
diff --git a/llvm/test/Transforms/InstCombine/or-select-factor.ll b/llvm/test/Transforms/InstCombine/or-select-factor.ll
new file mode 100644
index 0000000000000..75f62b2679ca0
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/or-select-factor.ll
@@ -0,0 +1,33 @@
+; RUN: opt -passes=instcombine -S < %s | FileCheck %s
+
+; Fold: (select C, (x | a), x) | b  ->  x | select C, (a | b), b
+
+define i8 @src(i8 %x, i8 %y) {
+; CHECK-LABEL: @src(
+; CHECK-NEXT:    [[V0:%.*]] = icmp eq i8 [[Y:%.*]], -1
+; CHECK-NEXT:    [[V1:%.*]] = or i8 [[X:%.*]], 4
+; CHECK-NEXT:    [[V2:%.*]] = select i1 [[V0]], i8 [[V1]], i8 [[X]]
+; CHECK-NEXT:    [[V3:%.*]] = or i8 [[V2]], 1
+; CHECK-NEXT:    ret i8 [[V3]]
+;
+  %v0 = icmp eq i8 %y, -1
+  %v1 = or i8 %x, 4
+  %v2 = select i1 %v0, i8 %v1, i8 %x
+  %v3 = or i8 %v2, 1
+  ret i8 %v3
+}
+
+define i8 @tgt(i8 %x, i8 %y) {
+; CHECK-LABEL: @tgt(
+; CHECK-NEXT:    [[V0:%.*]] = icmp eq i8 [[Y:%.*]], -1
+; CHECK-NEXT:    [[V1:%.*]] = select i1 [[V0]], i8 5, i8 1
+; CHECK-NEXT:    [[V2:%.*]] = or i8 [[X:%.*]], [[V1]]
+; CHECK-NEXT:    ret i8 [[V2]]
+;
+  %v0 = icmp eq i8 %y, -1
+  %v1 = select i1 %v0, i8 5, i8 1
+  %v2 = or i8 %x, %v1
+  ret i8 %v2
+}
+
+

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Miscompilation reproducer: https://alive2.llvm.org/ce/z/_v5Hjz

define i8 @src(i8 %x, i8 noundef %y) {
  %v0 = icmp samesign eq i8 %y, -1
  %v1 = or i8 %x, 4
  %v2 = select i1 %v0, i8 %v1, i8 %x
  %v31 = and i8 -2, %v2
  ret i8 %v31
}

define i8 @tgt(i8 %x, i8 noundef %y) {
  %v31 = or i8 %x, -2
  ret i8 %v31
}

Infinite loop reproducer:

; bin/opt -passes=instcombine reduced.ll -S
define i64 @func_48(i1 %cmp268, i64 %conv272) {
entry:
  br label %for.body200

for.body200:                                      ; preds = %for.body200, %entry
  %0 = phi i64 [ 0, %entry ], [ %or273, %for.body200 ]
  %and = and i64 1, %0
  %or253 = or i64 %and, 4398243566277173332
  %1 = select i1 %cmp268, i64 %or253, i64 %and
  %or273 = or i64 %1, %conv272
  br label %for.body200
}

@@ -0,0 +1,33 @@
; RUN: opt -passes=instcombine -S < %s | FileCheck %s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Use update_test_checks.py to generate test files.
  2. Add some negative tests.
  3. Add some multi-use tests.
  4. Do not add the tgt function. It should be generated by the script above.

Please read https://llvm.org/docs/InstCombineContributorGuide.html#tests for more information.

@@ -2695,6 +2695,40 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
if (Instruction *FoldedLogic = foldBinOpIntoSelectOrPhi(I))
return FoldedLogic;

// Factor a common operand across select feeding an 'or':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for duplicating the code in visitAnd?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,
while working on the optimization for the OR operation , I rewrite the expression from (select C, (X | A), X) | B to select C, (A | B),B. The same OR optimization can be used to rewrite AND operations as well. Instead of overengineering , I thought of duplicating it for AND operations.

if not required for AND operations, the code can be discarded.

@badwriter123 badwriter123 deleted the instcombine-missed-or-select-fold branch August 22, 2025 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missed optimization: fold (select C, (x | a), x) | b -> x | select C, (a | b), b
3 participants